from pathlib import Path
import random
import time
import itertools
import requests
import json
import re
import random
import threading

from bs4 import BeautifulSoup
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import TimeoutException
from onlinesimru import GetFree, GetRent, GetProxy, GetUser, GetNumbers, Driver

from browser import Browser


class Parser(Browser):

    """ Этот класс представляет парсер """

    def __init__(self, data, proxy_list=None):

        self.proxy_list = proxy_list
        self.display_browser = data[0]
        self.link_to_registration_page = data[2]
        self.api = data[3]
        self.number_threads = data[4]
        self.link_to_authorization_page = 'https://taxi.yandex.ru/'

        self.acktivator_info = {
            'token': '9868279a8350b1a96ddfc9e3bfe2fc440b8d0c43',
        }


    def wait(self, f=2, b=3.5):

        """ Чуть-чуть подождать """

        number = random.uniform(f, b)
        print(f'Ждем {number}\n')
        time.sleep(number)


    def convert_to_bs(self, browser):

        """ Получаем объект BeautifulSoup """

        html_source = browser.page_source
        page = BeautifulSoup(html_source, 'lxml')

        return page


    def get_saved_page(self, file_name):

        """ Открываем сохраненную страницу для парсинга и возвращаем ее """

        folder = Path('saved_html/')
        page = folder/file_name

        with open(page, 'r', encoding='utf-8') as file:

            text = file.read()
            page = BeautifulSoup(text, 'lxml')

            return page


    def get_configured_browser(self, proxy=None):

        """ Получаем настроенный браузер """

        if proxy:  # Получаем браузер с прокси

            proxy = random.choice(self.proxy_list)
            browser = self.get_browser(self.display_browser, proxy)

            return browser

        else:  # Если прокси не используюется

            return self.get_browser(self.display_browser, proxy)



    def enter_information_in_field(self, element_selenium, text, clear='yes'):

        """ Вводим информацию в поле """

        element_selenium.click()
        if clear == 'yes': element_selenium.clear()
        element_selenium.send_keys(text)

        self.wait(2, 3)


    def press_button(self, element_selenium):

        """ Нажимаем кнопку """

        element_selenium.click()    # Жмем на кнопку
        self.wait(.8, 1.2)          # Ждем


    def press_enter(self, browser, element_selenium):

        """ Нажимаем enter """

        element_selenium.send_keys(Keys.ENTER)


    def press_escape(self, browser):

        """ Нажимаем escape """

        ActionChains(browser).send_keys(Keys.ESCAPE).perform()

        self.wait(.5, .8)


    def get_list_name_and_surname(self):

        """ Получаем имена и фамилии """

        result = []

        with open('first_and_last_names.txt') as file:

            for name in file:

                name = name.strip()
                name = re.split(r'\s', name)
                result.append(name)

        return result


    def get_list_starting_addresses(self):

        """ Получаем список отправных адресов """

        result = []

        with open('starting_addresses.txt') as file:

            for line in file:

                starting_address = line.strip()
                result.append(starting_address)

        return result


    def get_list_destination_addresses(self):

        """ Получаем список конечных адресов """

        result = []

        with open('end_addresses.txt') as file:

            for line in file:

                end_address = line.strip()
                result.append(end_address)

        return result


    def get_list_tokens(self):

        """ Получаем список токенов """

        result = []

        with open('list_tokens.txt') as file:

            for line in file:

                token = line.strip()
                result.append(token)

        return result


    def get_proxy_list(self):

        """ Получаем список прокси """

        result = []

        with open('proxy.txt') as file:

            for line in file:

                proxy = line.strip()
                result.append(proxy)

        return result


    def complete_the_number(self, token, tzid):

        """ Завершаем номер """

        pass


    def check_whats_on_page(self, browser):

        """ Проверяем что на странице """

        try:

            element = browser.find_element(
                By.XPATH,
                '//h2[contains(text(), "Введите номер телефона для создания Яндекс ID")]'
            )

        except:

            return 'Не та страница'


    def finish_the_job(self, browser, token, tzid):

        """ Преждевременно закрываем браузер """

        browser.quit()

        link = f'https://onlinesim.ru/api/setOperationOk.php?apikey={token}&tzid={tzid}'
        requests.get(link)


    def perform_user_registration(self, browser, list_tokens, token, current_iteration):

        """ Получаем номер и код для авторизации """

        def get_number_for_authorization(browser, list_tokens, token, current_iteration):

            """ Получаем номер для авторизации """

            def get_number_onlinesim(browser, list_tokens, token, current_iteration):

                """ Получаем номер от сервиса onlinesim """

                numbers = GetNumbers(token)

                try:

                    tzid = numbers.get('Yandex')

                except Exception as e:

                    print(e)
                    browser.quit()
                    self.parser_wrapper(list_tokens, token, current_iteration)

                number = requests.get(f'https://onlinesim.ru/api/getState.php?apikey={token}').text
                number = json.loads(number)[0]['number']

                return ('onlinesim', numbers, tzid, number)


            def get_number_available_numbers_acktivator():

                """ Получаем количество доступных номеров """

                service = 'yandex'
                link = f'https://sms-acktiwator.ru/api/numbersstatus/{self.acktivator_info["token"]}?code=RU'

                r = requests.get(link).text
                data = json.loads(r)
                number_available_numbers = ''

                for item in data:

                    if item['name'] == service:

                        number_available_numbers += str(item['count'])

                return int(number_available_numbers)


            def get_number_acktivator():

                """ Получаем номер от сервиса acktivator """

                link = f'https://sms-acktiwator.ru/api/getnumber/{self.acktivator_info["token"]}?id=15&code=RU'

                r = requests.get(link).text
                data = json.loads(r)

                return data


            # ВЫШЕ ОПРЕДЕЛЕНИЕ ФУНКЦИЙ

            if self.api == 'onlinesim':    # Работаем с onlinesim

                data = get_number_onlinesim(browser, list_tokens, token, current_iteration)

                return data

            if self.api == 'acktivator':    # Работаем с acktivator

                number_available_numbers = get_number_available_numbers_acktivator()

                if number_available_numbers == 0: return 'Номеров нет'

                data = get_number_acktivator()

                return ('acktivator', data['id'], data['number'])


        def get_code_for_registration(tzid):

            """ Получаем код для регистрации """

            def get_code_onlinesim(tzid):

                """ Получаем код от серваиса onlinesim """

                try:

                    code = numbers.wait_code(tzid)                    # Ждем код

                    return code

                except:

                    print('Код не пришел 1')
                    get_code_again()
                    return 'Код не пришел'


            def get_code_acktivator(tzid):

                """ Получаем код от сервиса acktivator """

                while True:

                    link = f'https://sms-acktiwator.ru/api/getlatestcode/{self.acktivator_info["token"]}?id={tzid}'
                    r = requests.get(link).text

                    if r: return r

                    time.sleep(1)


            # ВЫШЕ ОПРЕДЕЛЕНИЕ ФУНКЦИЙ

            if self.api == 'onlinesim':

                code = get_code_onlinesim(tzid)

                return code

            if self.api == 'acktivator':

                code = get_code_acktivator(tzid)

                return code



        def specify_number_on_website(browser, number):

            """ Указываем номер на сайте """

            def configure_number_to_enter(browser, field_for_entering_number_selenium, number):

                """ Проверяем что находится в поле """

                well_what = field_for_entering_number_selenium.get_attribute('value')

                if well_what:

                    return number[2:]

                else:

                    return number


            # ВЫШЕ ОПРЕДЕЛЕНИЕ ФУНКЦИЙ

            field_for_entering_number_selenium = browser.find_element(
                By.XPATH,
                '//input[@name="phone"]'
            )

            number = configure_number_to_enter(browser, field_for_entering_number_selenium, number)
            self.enter_information_in_field(field_for_entering_number_selenium, number, clear='no')

            next_button = browser.find_element(
                By.XPATH,
                '//div[@class="Phone-controls"]/button'
            )
            self.press_button(next_button)


        def check_for_an_error_on_limit(browser):

            """ Проверяем появилась ли ошибка по лимиту """

            def find_out_limit_exceeded(browser):

                """ Узнаем, превышает ли лимит звонков или сообщение """

                try:

                    element_selenium = browser.find_element(
                        By.XPATH,
                        '//div[contains(text(), "Превышен лимит")]'
                    )

                    input('Дальше?')
                    return 'Превышен лимит'

                except:

                    return 'Неизвестно'


            def check_they_calling(browser):

                """ Проверяем не звонят ли """

                try:

                    element_selenium = browser.find_element(
                        By.XPATH,
                        '//div[contains(text(), "Звоним вам… введите последние четыре цифры номера, с которого поступит звонок")]'
                    )

                    input('Звонят?')
                    return 'Звонят'

                except:

                    return 'Неизвестно'


            # ВЫШЕ ОПРЕДЕЛЕНИЕ ФУНКЦИЙ

            count = 0

            while True:

                if count == 180: return 'Код не пришел'

                well_what_limit = find_out_limit_exceeded(browser)
                if well_what_limit != 'Неизвестно':

                    return well_what_limit

                print('Превышения по лимитам звонков нет')

                well_what_calling = check_they_calling(browser)
                if well_what_calling != 'Неизвестно':

                    return well_what_calling

                print('Не звонят для подтверждения личности')

                count += 1
                print(count)
                time.sleep(.5)


        def get_code_again(token, tzid):

            """ Получаем код повторно """

            link = f'https://onlinesim.ru/api/setOperationRevise.php?apikey={token}&tzid={tzid}'
            r = requests.get(link).text

            print('r')
            print(r)
            print('r')


        def specify_code_on_site(browser, code):

            """ Вводим код """

            # Вводим код
            field_for_entering_code = browser.find_element(
                By.XPATH,
                '//input[@name="phoneCode"]'
            )
            self.enter_information_in_field(field_for_entering_code, code)

            # Нажимаем далее
            next_button = browser.find_element(
                By.XPATH,
                '//button[1]'
            )
            self.press_button(next_button)


        def specify_name_on_website(browser, name):

            """ Указываем имя """

            # Вводим имя
            field_for_entering_name = browser.find_element(
                By.XPATH,
                '//input[@name="firstname"]'
            )
            self.enter_information_in_field(field_for_entering_name, name)


        def specify_surname_on_website(browser, surname):

            """ Указываем фамилию """

            # Вводим фамилию
            field_for_entering_surname = browser.find_element(
                By.XPATH,
                '//input[@name="lastname"]'
            )
            self.enter_information_in_field(field_for_entering_surname, surname)

            # Нажимаем далее
            next_button = browser.find_element(
                By.XPATH,
                '//button[1]'
            )
            self.press_button(next_button)


        def confirm_user_agreement(browser):

            """ Подтверждамем пользовательское соглашение """

            next_button = browser.find_element(
                By.XPATH,
                '//button'
            )
            self.press_button(next_button)


        def get_name_and_surname():

            """ Получаем имя и фамилию """

            list_name_and_surname = self.get_list_name_and_surname()
            name_and_surname = random.choice(list_name_and_surname)
            name = name_and_surname[1]
            surname = name_and_surname[0]

            return (name, surname)


        # ВЫШЕ ОПРЕДЕЛЕНИЕ ФУНКЦИЙ

        data = get_number_for_authorization(browser, list_tokens, token, current_iteration)    # Получаем номер для авторизации

        print('data')
        print(data)
        print('data')

        if data == 'Номеров нет': return 'Номеров нет'    # Если номеров нет

        if 'onlinesim' in data[0]:                        # Если работаем с onlinesim

            name, numbers, tzid, number = data

        if 'acktivator' in data[0]:                       # Если работаем с acktivator

            name, tzid, number = data

        print(f'Получили номер - {number}')

        specify_number_on_website(browser, number)       # Указываем номер в форму
        print('Указали номер, ждем код...')
        #input('Ввели номер, продолжаем?')

        well_what_limit = check_for_an_error_on_limit(browser)
        if (self.api == 'onlinesim' and well_what_limit == 'Превышен лимит') or (self.api == 'onlinesim' and well_what_limit == 'Звонят') or (self.api == 'onlinesim' and well_what_limit == 'Код не пришел'):

            print('Проблемы с получение кода, завершаем все')
            self.finish_the_job(browser, token, tzid)

            return 'Превышен лимит'

        code = get_code_for_registration(tzid)
        print(f'Код пришел - {code}')

        specify_code_on_site(browser, code)              # Указываем код в форму
        print(f'Указали код')
        input('Ввели код, продолжаем?')

        name, surname = get_name_and_surname()

        specify_name_on_website(browser, name)                 # Указываем имя
        specify_surname_on_website(browser, surname)              # Указываем фамилию
        print('Указали имя и фамилию')
        input('Ввели имя и фамилию, продолжаем?')

        confirm_user_agreement(browser)                  # Подтвердили пользовательское соглашение
        print('Подтвердили пользовательское соглашение')

        time.sleep(5)

        return [token, tzid]


    def make_an_order(self, browser, token, tzid):

        """ Делаем заказ """

        def get_text_input_fields(browser):

            """ Получаем поля для ввода текста """

            text_input_fields_selenium = browser.find_elements(
                By.XPATH,
                '//textarea[@class="Textarea-Control"]'
            )

            where_from = text_input_fields_selenium[0]
            where = text_input_fields_selenium[1]
            comment_to_driver = text_input_fields_selenium[3]

            return (where_from, where, comment_to_driver)


        def specify_comment_to_driver(browser, comment_to_driver_selenium):

            """ Указываем комментарий водителю """

            element_to_move = browser.find_elements(
                By.XPATH,
                '//ul[@class="List-Container"]//button'
            )[-1]

            action = ActionChains(browser)
            action.move_to_element(element_to_move)
            action.perform()

            self.enter_information_in_field(comment_to_driver_selenium, 'Комментарий водителю')


        def check_availability_of_car(browser):

            """ Проверяем наличие машины """

            while True:

                try:

                    element_selenium = browser.find_element(
                        By.XPATH,
                        '//div[contains(text(), "К сожалению, на текущий момент нет свободных машин")]'
                    )

                    print('Машины нет')
                    input('Дальше?')
                    self.finish_the_job(browser)

                    return 'Машины нет'

                except:

                    waiting_element_selenium = browser.find_element(
                        By.XPATH,
                        '//div[contains(text(), "Отменить")]/..'
                    )

                    return 'Машина попалась'

                self.wait(.4, .5)


        def work_with_wating_time(browser):

            """ Получаем время ожидания """

            def get_waiting_time(wating_time_text):

                """ Получаем время ожидания """

                time = int(re.findall(r'\d+', wating_time_text)[0]) + 2

                return time


            # ВЫШЕ ОПРЕДЕЛЕНИЕ ФУНКЦИЙ

            while True:

                try:

                    wating_time_selenium = browser.find_element(
                        By.XPATH,
                        '//div[contains(text(), "Звонок")]/../../../..//div[contains(text(), "Через")]'
                    )
                    wating_time_text = wating_time_selenium.text
                    print(f'Изначальное время - {wating_time_text}')

                    wating_time = get_waiting_time(wating_time_text)

                    return wating_time

                except:

                    time.sleep(.5)


        def wait_for_page_to_load(browser):

            """ Ожидаем загрузки элемента """

            element_pressent = EC.presence_of_element_located((
                By.XPATH,
                '//textarea[@placeholder="Комментарий водителю..."]'
            ))

            WebDriverWait(browser, 10).until(element_pressent)


        def confirm_the_order(browser):

            """ Подтверждаем заказ """

            order_button = browser.find_element(
                By.XPATH,
                '//span[contains(text(), "Заказать")]/..'
            )
            self.press_button(order_button)


        def check_if_there_is_driver(browser):

            """ Проверяем есть ли водитель """

            try:

                pass

            except:

                pass


        def cancel_an_order(browser):

            """ Ожидаем появления элемента """

            def get_cancel_button(browser):

                """ Пытаемся получить кнопку отмены """

                try:

                    waiting_element_selenium = browser.find_element(
                        By.XPATH,
                        '//div[contains(text(), "Отменить")]/..'
                    )
                    self.press_button(waiting_element_selenium)

                    return 'Кнопка отмены'

                except:

                    print('Кнопки отмены нет')
                    return 'Неизвестно'


            def find_out_there_is_driver(browser):

                """ Узнаем есть ли водитель """

                try:

                    element_selenium = browser.find_element(
                        By.XPATH,
                        '//div[contains(text(), "К сожалению, на текущий момент нет свободных машин")]'
                    )

                    return 'Машины нет'

                except:

                    print('Кнопки к содлоыаводлыв нет')
                    return 'Неизвестно'


            # ВЫШЕ ОПРЕДЕЛЕНИЕ ФУНКЦИИ

            while True:

                well_what_cancel_button = get_cancel_button(browser)
                if well_what_cancel_button != 'Неизвестно':

                    return 'Кнопка отмены'

                well_what_driver = find_out_there_is_driver(browser)
                if well_what_driver != 'Неизвестно':

                    return 'Машины нет'

                time.sleep(.5)


        def confirm_cancellation(browser):

            """ Подтверждаем отмену заказа """

            confirm_cancel_button_selenium = browser.find_element(
                By.XPATH,
                '//span[contains(text(), "Да")]/..'
            )
            self.press_button(confirm_cancel_button_selenium)


        def click_ok(browser):

            """ Нажимаем понятно """

            while True:

                try:

                    ok_button_selenium = browser.find_element(
                        By.XPATH,
                        '//span[contains(text(), "Понятно")]/..'
                    )
                    self.press_button(ok_button_selenium)

                    break

                except:

                    print('Кнопки понятно пока нет')


        def choose_a_car(browser):

            """ Выбираем машину для заказа """

            car_button_selenium = browser.find_element(
                By.XPATH,
                '//img[@alt="Эконом"]/../..'
            )
            self.press_button(car_button_selenium)
            self.press_escape(browser)


        # ВЫШЕ ОПРЕДЕЛЕНИЕ ФУНКЦИЙ

        list_starting_addresses = self.get_list_starting_addresses()
        list_destination_addresses = self.get_list_destination_addresses()

        starting_address = random.choice(list_starting_addresses)          # Стартовый адрес
        destination_address = random.choice(list_destination_addresses)    # Конечный адрес

        browser.get(self.link_to_authorization_page)
        wait_for_page_to_load(browser)

        where_from, where, comment_to_driver_selenium = get_text_input_fields(browser)

        self.enter_information_in_field(where_from, starting_address)     # Вводим начальный адрес
        self.press_enter(browser, where_from)
        #input('Указали 1 адрес, продолжаем?')

        self.enter_information_in_field(where, destination_address)       # Вводим конечный адрес
        self.press_enter(browser, where)
        #input('Указали 2 адрес, продолжаем?')

        choose_a_car(browser)    # Выбрали машину
        #input('Выбрали машину, продолжаем?')

        specify_comment_to_driver(browser, comment_to_driver_selenium)    # Вводим комментарий водителю
        #input('Указали комментарий, продолжаем?')

        confirm_the_order(browser)       # Подтверждаем заказ
        #input('Подтвердили заказ, продолжаем?')

#        result_checking_car = check_availability_of_car(browser)    # Проверяем есть ли машина
#        if result_checking_car == 'Машины нет': return

        t = work_with_wating_time(browser)

        time.sleep(t)    # Ожидаем
        print('time')
        print(t)
        print('time')

        what = cancel_an_order(browser)         # Отменяем заказ
        print('what')
        print(what)
        print('what')
        #input('Отменили заказ, продолжаем?')

        confirm_cancellation(browser)    # Подтверждаем отмену
        #input('Подтвердили отмену, продолжаем?')

        click_ok(browser)                # Нажимаем кнопку понятно
        #input('Нажали ок, продолжаем?')

        print('Закончили')

        browser.quit()


    def parser_wrapper(self, proxy_list, list_tokens=None, token=None, current_iteration=None):

        """ Обертка парсера """

        if self.api == 'onlinesim':

            token = list_tokens[current_iteration]    # Получаем токен

        while True:

            browser = self.get_configured_browser()        # Получаем браузер
            browser.get('https://api.ipify.org/')          # Открываем страницу
            time.sleep(5)                                  # Чуть ждем
            browser.get(self.link_to_registration_page)    # Открываем страницу

            well_what = self.check_whats_on_page(browser)    # Проверяем что за страница

            if not well_what == 'Не та страница': break

            browser.quit()

        result = self.perform_user_registration(browser, list_tokens, token, current_iteration)    # Выполняем регистрацию пользователя
        if result == 'Код не пришел' or result == 'Превышен лимит':

            input('Дальше?')
            print('Преждевременный выход')
            return

        token, tzid = result
        result = self.make_an_order(browser, token, tzid)                       # Делаем заказ
        if result == 'Машины нет': return


    def start_parsing(self):

        """ Начать парсинг """

        list_tokens = self.get_list_tokens()    # Получаем список токенов
        proxy_list = self.get_proxy_list()

        if self.api == 'onlinesim':

            for (current_iteration, token) in enumerate(list_tokens):

                t = threading.Thread(target=self.parser_wrapper, args=(proxy_list, list_tokens, token, current_iteration))
                t.start()

        if self.api == 'acktivator':

            for item in range(self.number_threads):

                t = threading.Thread(target=self.parser_wrapper)
                t.start()
